package edu.scnu.fans.fingertip.filter;

import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import edu.scnu.fans.fingertip.common.api.ApiResponse;
import edu.scnu.fans.fingertip.common.api.CommonConstant;
import edu.scnu.fans.fingertip.common.api.HeaderConstant;
import edu.scnu.fans.fingertip.common.web.ApiResponseUtils;
import edu.scnu.fans.fingertip.config.AuthUrlConfig;
import edu.scnu.fans.fingertip.user.api.UserApi;
import edu.scnu.fans.fingertip.user.output.VerifyTokenOutput;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Optional;

@Component
public class AuthFilter extends ZuulFilter {

    @Resource
    private AuthUrlConfig authUrlConfig;

    @Resource
    private UserApi userApi;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String requestURI = request.getRequestURI();

        // 恶意请求
        if (request.getHeader(HeaderConstant.USER_ID) != null) {
            this.response401(ctx);
            return null;
        }

        String token = request.getHeader("token");
        Long userId = Optional.ofNullable(stringRedisTemplate.opsForValue().get(CommonConstant.TOKEN_REDIS_KEY + token))
                .map(Long::valueOf)
                .orElse(null);

        List<String> exclude = authUrlConfig.getExclude();
        AntPathMatcher matcher = new AntPathMatcher();

        boolean shouldExclude = exclude.stream().anyMatch(urlConfig -> matcher.match(urlConfig, requestURI));

        // 需要检验token 正确性的
        if (!shouldExclude) {

            // 没有 token
            if (StringUtils.isBlank(token)) {
                this.response401(ctx);
                return null;
            }

            // token 无效
            if (userId == null) {
                this.response401(ctx);
                return null;
            }

            // 验证 token 有效性
            VerifyTokenOutput verifyTokenResult = ApiResponseUtils.codeOkGetData(userApi.verifyToken(userId, token));
            if (!verifyTokenResult.isSuccess()) {
                this.response401(ctx);
                return null;
            }
        }

        // 微服务内部设置消息头
        if (userId != null) {
            ctx.addZuulRequestHeader(HeaderConstant.USER_ID, userId.toString());
        }

        return null;
    }

    /**
     * 响应401
     */
    private void response401(RequestContext ctx) {
        ctx.addZuulResponseHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        ctx.setSendZuulResponse(false);
        ctx.setResponseStatusCode(401);
        ctx.setResponseBody(JSON.toJSONString(new ApiResponse<>(401, "token失效", new Object())));
    }
}
